package org.peoplesearch.peopleidcollector;


import org.peoplesearch.conf.Commons;
import org.peoplesearch.conf.PeopleIdCollectorStore;
import org.peoplesearch.storer.IdStore;
import org.peoplesearch.storer.SetIdStore;
import org.peoplesearch.vk.api.androidvksdk.KException;
import org.peoplesearch.vk.api.androidvksdk.User;
import org.peoplesearch.vk.processor.Credentials;
import org.peoplesearch.vk.processor.VkAPiExecutor;
import org.peoplesearch.vk.processor.VkCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Collector {

    final static Logger logger = LoggerFactory.getLogger(Collector.class);

    private final VkAPiExecutor vkAPiExecutor;
    private volatile boolean which = true;

    public Collector(VkAPiExecutor vkAPiExecutor) {
        this.vkAPiExecutor = vkAPiExecutor;
    }


    public static void main(String[] args) {
        VkAPiExecutor executor = new VkAPiExecutor(new Credentials());
        Collector collector = new Collector(executor);
        List<Filter> filters = new ArrayList<Filter>();
        filters.add(new Filter() {
            @Override
            public void filter(Long id, VkAPiExecutor executor, IdStore store) {
                store.put(id);
                logger.trace("id puted in all");
            }

            @Override
            public String getName() {
                return "all";
            }
        });
/*        filters.add(new Filter() {
            @Override
            public void filter(final Long id, VkAPiExecutor executor, final IdStore store) {
                final ArrayList<User> users = new ArrayList<User>();
                executor.addCommand(new VkCommand() {
                    @Override
                    public void run() {
                        try {
                            users.addAll(api.getProfiles(new ArrayList<Long>(Arrays.asList(new Long[]{id})), null, null, "nom"));
                            if ((users.size() == 1)
                                    && (users.get(0).city != null)
                                    && (users.get(0).graduation != null)
                                    && (users.get(0).sex != null)
                                    && (users.get(0).sex == 1)
                                    && (users.get(0).city == 2)
                                    && ((users.get(0).graduation >= 2012) && (users.get(0).graduation <= 2015))) {
                                store.put(id);
                                logger.trace("id puted in likeporno");
                            }
                        } catch (Exception e) {
                            logger.error(e.toString());
                        }
                    }
                });
            }

            @Override
            public String getName() {
                return "likeapornoquery";
            }
        });*/
        collector.collectId(filters, 7359953L);

    }

    private BufferedWriter getRezultIdStore(String fileName) {
        BufferedWriter store;
        try {
            store = new BufferedWriter(new FileWriter(new File(Commons.DATA_DIR + fileName + "_" + which)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return store;
    }

    private void storeId(IdStore idStore, String fileName) {
        BufferedWriter writer = getRezultIdStore(fileName);
        try {
            writer.write(idStore.size() + "\n");
            for (Long id : idStore) {
                writer.write(id + "\n");
            }
            writer.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void switchFiles() {
        if (which == true) {
            which = false;
        } else which = true;
    }

    public void collectId(List<Filter> filters, Long seed) {

        logger.debug("Seed is " + seed);
        final List<FilterStore> filterStores = Collections.synchronizedList(new ArrayList<FilterStore>());
        final Queue<Long> queue = new ConcurrentLinkedQueue<Long>();
        final Queue<VkCommand> commands = new ConcurrentLinkedQueue<VkCommand>();
        final Set<Long> seen = Collections.synchronizedSet(new HashSet<Long>());
        final AtomicInteger commandsCount = new AtomicInteger();

        for (Filter filter : filters) {
            filterStores.add(new FilterStore(filter, new SetIdStore()));
        }

        queue.add(seed);
        final VkCommand collectorCommand = new VkCommand() {
            public void run() {
                int commandId = commandsCount.incrementAndGet();
                String commandPrefix = "Command number " + commandId + " ";
                Long current = queue.poll();
                if (commandId % 100 == 0) logger.debug(commandPrefix + "In this command current id is " + current);
                try {
                    /* List<User> users = api.getFriends(current, null, null);*/
                    List<Long> users = api.getOnlineFriends(current);
                    logger.trace(commandPrefix + " got user id's. Id's : " + users.size());
                    for (Long user : users) {
                        Long currentUID = user;
                        if (!seen.contains(currentUID)) {
                            seen.add(currentUID);
                            queue.add(currentUID);
                            commands.add(this);
                            for (final FilterStore filterStore : filterStores) {
                                filterStore.filter.filter(currentUID, vkAPiExecutor, filterStore.idStore);
                            }
                        }
                        logger.trace("Addded user " + currentUID + ". number of commands is " + commands.size());
                    }
                } catch (Exception e) {
                    logger.error(e.toString());
                    if (!((e instanceof KException) && ((KException) e).error_code == 15)) {
                        e.printStackTrace();
                        System.exit(-1);
                    }
                }
            }
        };

        commands.add(collectorCommand);

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    logger.debug("Started storing in the files");
                    for (FilterStore filterStore : filterStores) {
                        storeId(filterStore.idStore, filterStore.filter.getName());
                        logger.debug("Done filter " + filterStore.filter.getName());
                    }
                    switchFiles();
                    logger.debug("Completed storing in the files");
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(-1);
                }
            }
        }, 20, 300, TimeUnit.SECONDS);

        while (commands.size() > 0) {
            while (commands.size() > 0) {
                if (vkAPiExecutor.getTasksInQueue() > 100500) {
                    vkAPiExecutor.waitForCompletion();
                }
                vkAPiExecutor.addCommand(commands.poll());
            }
            vkAPiExecutor.waitForCompletion();
        }
        vkAPiExecutor.terminate(100);
    }

    private class FilterStore {
        public Filter filter;
        public IdStore idStore;

        public FilterStore(Filter filter, IdStore storeId) {
            this.idStore = storeId;
            this.filter = filter;
        }
    }


}
